﻿using S7.Net;
using Sunny.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WCS.Controls.RunLog;
using WCS.Entity;

namespace WCS.Helpers.PLCOperation
{
    public class S7Plc
    {
        private Plc plcMan = null;

        public S7Plc() { }

        static readonly object PlcLock = new object();

        public S7Plc(CpuType cpuType, string Ip)
        {
            plcMan = new Plc(cpuType, Ip, 0, 1);
        }

        /// <summary>
        /// 连接PLC
        /// </summary>
        /// <returns></returns>
        public S7PlcResult ConnectServer()
        {
            try
            {
                if (!plcMan.IsConnected)
                {
                    lock (PlcLock)
                    {
                        if (!plcMan.IsConnected)
                        {
                            plcMan.Open();
                        }
                    }
                }
                return new S7PlcResult { IsSuccess = true };
            }
            catch (Exception ex)
            {
                return new S7PlcResult { IsSuccess = false, Message = ex.Message };
            }
        }

        /// <summary>
        /// 写入PLC
        /// </summary>
        /// <param name="PlcAddress">DB块地址</param>
        /// <param name="writeData">读取长度</param>
        /// <returns></returns>
        public S7PlcResult S7PlcWiter(string PlcAddress, byte[] writeData)
        {
            try
            {
                var result = PlcVerification(plcMan, PlcAddress);

                if (!result.IsSuccess)
                {
                    return result;
                }

                plcMan.WriteBytes(DataType.DataBlock, result.DBBlock, result.Start, writeData);

                return new S7PlcResult { IsSuccess = true };
            }
            catch (Exception ex)
            {
                RunLog.runLog.Error($@"异常信息【{ex.Message}】【{ex.StackTrace}】");
                return new S7PlcResult { IsSuccess = false, Message = ex.Message };
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public S7PlcResult S7PlcWiterString(string PlcAddress,string writeData)
        {
            try
            {
                var result = PlcVerification(plcMan, PlcAddress);

                if (!result.IsSuccess)
                {
                    return result;
                }

                var temp = Encoding.ASCII.GetBytes(writeData);

                var bytes = S7.Net.Types.S7String.ToByteArray(writeData, temp.Length);

                plcMan.WriteBytes(DataType.DataBlock, result.DBBlock, result.Start, bytes);

                return new S7PlcResult { IsSuccess = true };
            }
            catch (Exception ex)
            {
                RunLog.runLog.Error($@"异常信息【{ex.Message}】【{ex.StackTrace}】");
                return new S7PlcResult { IsSuccess = false, Message = ex.Message };
            }
        }

        /// <summary>
        /// 写入PLC
        /// </summary>
        /// <param name="PlcAddress">DB块地址</param>
        /// <param name="writeData">读取长度</param>
        /// <returns></returns>
        public S7PlcResult S7PlcWiterBool(string PlcAddress, bool value)
        {
            try
            {
                var result = PlcVerification(plcMan, PlcAddress);

                if (!result.IsSuccess)
                {
                    return result;
                }

                plcMan.Write(DataType.DataBlock, result.DBBlock, result.Start, value, result.bitAdr);

                return new S7PlcResult { IsSuccess = true };
            }
            catch (Exception ex)
            {
                RunLog.runLog.Error($@"异常信息【{ex.Message}】【{ex.StackTrace}】");
                return new S7PlcResult { IsSuccess = false, Message = ex.Message };
            }
        }

        /// <summary>
        /// 读取PLC
        /// </summary>
        /// <param name="PlcAddress"></param>
        /// <param name="lenght"></param>
        /// <returns></returns>
        public S7PlcReadResult<int> S7PlcReadInt(string PlcAddress, int lenght)
        {
            try
            {
                var result = PlcVerification(plcMan, PlcAddress);

                if (!result.IsSuccess)
                {
                    S7PlcReadResult<int> s7PlcReadResult = new S7PlcReadResult<int>();
                    s7PlcReadResult.MapperFrom(result);
                    return s7PlcReadResult;
                }
                var Content = plcMan.ReadBytes(DataType.DataBlock, result.DBBlock, result.Start, lenght);
                short[] a = new short[lenght];
                if (lenght == 2)
                {
                    return new S7PlcReadResult<int> { IsSuccess = true, Content = BitConverter.ToInt16(Content.Reverse().ToArray(), 0) };
                }
                else if (lenght == 4)
                {
                    return new S7PlcReadResult<int> { IsSuccess = true, Content = BitConverter.ToInt32(Content.Reverse().ToArray(), 0) };
                }
                else
                {
                    return new S7PlcReadResult<int> { IsSuccess = false, Message = "读取长度输入有误" };
                }
            }
            catch (Exception ex)
            {
                RunLog.runLog.Error($@"异常信息【{ex.Message}】【{ex.StackTrace}】");
                return new S7PlcReadResult<int> { IsSuccess = false, Message = ex.Message };
            }
        }

        /// <summary>
        /// 读取PLC
        /// </summary>
        /// <param name="PlcAddress"></param>
        /// <param name="lenght"></param>
        /// <returns></returns>
        public S7PlcReadResult<bool> S7PlcReadBool(string PlcAddress)
        {
            try
            {
                var result = PlcVerification(plcMan, PlcAddress);

                if (!result.IsSuccess)
                {
                    S7PlcReadResult<bool> s7PlcReadResult = new S7PlcReadResult<bool>();
                    s7PlcReadResult.MapperFrom(result);
                    return s7PlcReadResult;
                }

                var Content = (bool)plcMan.Read(DataType.DataBlock, result.DBBlock, result.Start, VarType.Bit, 1, (byte)result.bitAdr);
                return new S7PlcReadResult<bool> { IsSuccess = true, Content = Content };
            }
            catch (Exception ex)
            {
                RunLog.runLog.Error($@"异常信息【{ex.Message}】【{ex.StackTrace}】");
                return new S7PlcReadResult<bool> { IsSuccess = false, Message = ex.Message };
            }
        }

        /// <summary>
        /// 读取PLC
        /// </summary>
        /// <param name="PlcAddress"></param>
        /// <param name="lenght"></param>
        /// <returns></returns>
        public S7PlcReadResult<byte[]> S7PlcRead(string PlcAddress, int lenght)
        {
            try
            {
                var result = PlcVerification(plcMan, PlcAddress);

                if (!result.IsSuccess)
                {
                    S7PlcReadResult<byte[]> s7PlcReadResult = new S7PlcReadResult<byte[]>();
                    s7PlcReadResult.MapperFrom(result);
                    return s7PlcReadResult;
                }

                var Content = plcMan.ReadBytes(DataType.DataBlock, result.DBBlock, result.Start, lenght);
                return new S7PlcReadResult<byte[]> { IsSuccess = true, Content = Content };
            }
            catch (Exception ex)
            {
                RunLog.runLog.Error($@"异常信息【{ex.Message}】【{ex.StackTrace}】");
                return new S7PlcReadResult<byte[]> { IsSuccess = false, Message = ex.Message };
            }
        }

        /// <summary>
        /// 读取PLC
        /// </summary>
        /// <param name="PlcAddress"></param>
        /// <param name="lenght"></param>
        /// <returns></returns>
        public S7PlcReadResult<string> S7PlcReadString(string PlcAddress)
        {
            try
            {
                var result = PlcVerification(plcMan, PlcAddress);

                if (!result.IsSuccess)
                {
                    S7PlcReadResult<string> s7PlcReadResult = new S7PlcReadResult<string>();
                    s7PlcReadResult.MapperFrom(result);
                    return s7PlcReadResult;
                }
                var reservedLength = (byte)plcMan.Read(DataType.DataBlock, result.DBBlock, result.Start, VarType.Byte, 1);

                var Content = plcMan.Read(DataType.DataBlock, result.DBBlock, result.Start, VarType.S7String, reservedLength);
                return new S7PlcReadResult<string> { IsSuccess = true, Content = Convert.ToString(Content) };
            }
            catch (Exception ex)
            {
                RunLog.runLog.Error($@"异常信息【{ex.Message}】【{ex.StackTrace}】");
                return new S7PlcReadResult<string> { IsSuccess = false, Message = ex.Message };
            }
        }

        /// <summary>
        /// 校验PLC 和地址
        /// </summary>
        /// <param name="plc"></param>
        /// <returns></returns>
        public S7PlcResult PlcVerification(Plc plc, string PlcAddress)
        {
            try
            {
                if (plc == null)
                {
                    return new S7PlcResult { IsSuccess = false, ErrorCode = -1, Message = "PLC未初始化" };
                }

                var plcAdr = AdrHandle(PlcAddress);

                if (plcAdr == null)
                {
                    return new S7PlcResult { IsSuccess = false, Message = "PLC地址处理有误" };
                }
                var s7PlcResult = new S7PlcResult
                {
                    IsSuccess = true,
                    DBBlock = plcAdr.DBBlock,
                    Start = plcAdr.Start,
                    bitAdr = plcAdr.bitAdr
                };
                if (!plcMan.IsConnected)
                {
                    RunLog.runLog.Warning($@"PLC【{plcMan.IP}】连接中断，正在重新连接");

                    var open = ConnectServer();
                    if (!open.IsSuccess)
                    {
                        //PLC已断开
                        return new S7PlcResult { IsSuccess = false, ErrorCode = -1, Message = "PLC已断开" };
                    }
                    return s7PlcResult;
                }

                return s7PlcResult;
            }
            catch (Exception ex)
            {
                return new S7PlcResult { IsSuccess = false, Message = ex.Message };
            }
        }

        /// <summary>
        /// PLC地址处理  DB32.47       DB32.47.1
        /// </summary>
        /// <returns></returns>
        public S7PlcResult AdrHandle(string PlcAddress)
        {
            try
            {
                S7PlcResult s7PlcResult = new S7PlcResult();
                var strs = PlcAddress.Substring(2).Split(".");

                s7PlcResult.DBBlock = Convert.ToInt32(strs[0]);
                s7PlcResult.Start = Convert.ToInt32(strs[1]);
                if (strs.Length == 3)
                {
                    s7PlcResult.bitAdr = Convert.ToInt32(strs[2]);
                }

                return s7PlcResult;
            }
            catch (Exception ex)
            {
                return new S7PlcResult { IsSuccess = false, Message = ex.Message };
            }
        }

        /// <summary>
        /// Bit转Int
        /// </summary>
        /// <param name="src"></param>
        /// <param name="offset"></param>
        /// <returns></returns>
        private int BytesToInt(byte[] src, int offset = 0)
        {
            int value;
            value = (int)(((src[offset] & 0xFF) << 24)
                    | ((src[offset + 1] & 0xFF) << 16)
                    | ((src[offset + 2] & 0xFF) << 8)
                    | (src[offset + 3] & 0xFF));
            return value;
        }
    }

    public class S7PlcResult
    {
        public bool IsSuccess { get; set; }
        public string Message { get; set; }
        public int ErrorCode { get; set; }
        public int DBBlock { get; set; }
        public int Start { get; set; }
        public int bitAdr { get; set; }
    }

    public class S7PlcReadResult<T> : S7PlcResult
    {
        public T Content { get; set; }
    }
}
